package org.ilc.socketserver;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.ilc.Bootstrap;
import org.ilc.Constants;
import org.ilc.FileLogger;
import org.ilc.push.IHeader;
import org.ilc.push.ISession;
import org.ilc.push.exception.PushException;

import java.io.IOException;

/**
 * @author malongbo
 */
public class SessionHandler extends IoHandlerAdapter {
    private final static Log log = LogFactory.getLog ("PushHandler");

    private final static String SESSION_KEY = "session";
    private final static String USER_KEY = "user";
    private Acceptor acceptor;

    public SessionHandler(Acceptor acceptor) {
        this.acceptor = acceptor;
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        if (log.isInfoEnabled())
            log.info("a connection created....");
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        String authUser=(String)session.getAttribute(USER_KEY);

        if (authUser == null)
            return;

        if (log.isInfoEnabled()) {
            log.info(new StringBuffer().append(authUser).append(
                    ", connection is closed"));
        }

        if(Bootstrap.getContext().getAppConfig().getBoolean(Constants.SERVER_DEBUG)) {
            new FileLogger().log(SessionHandler.class, authUser + " logout.");
        }

        try {
            long id = this.acceptor.getSessionId(authUser);
            //如果是同一个会话
            if(id == session.getId())
                this.acceptor.removeMapping(authUser);

        } catch (Exception e) {
            if (log.isErrorEnabled ()) {
                log.error ("移除会话映射出错", e);
            }
        }
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        try {
            String user =(String) session.getAttribute(USER_KEY);

            if (log.isInfoEnabled()) {
                log.info(new StringBuffer().append(user).append(
                        " recive sessionIdle event"));
            }

            if (status == IdleStatus.BOTH_IDLE) {
                this.sessionClosed(session);
            } else {
                super.sessionIdle(session, status);
            }
        } catch (Exception e) {
            if (log.isErrorEnabled ()) {
                log.error (e);
            }
        }
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        if (log.isErrorEnabled ()) {
            log.error (new StringBuffer("socket error: ").append (cause.getMessage ()), cause);
        }
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        if(log.isInfoEnabled()){
            log.debug("push server received message from " + session.getRemoteAddress ());
        }

        Request request = null;
        Response response = null;

        try {
            response = new Response (session);
            request = (Request) message;

            /*
            无效用户，向客户端响应错误
             */
            if (null == request) {
                throw new PushException("unvalid request", IHeader.SC_INVALID);
            }

            if (null == request.getHeader().getAuthUser()) {
                throw new PushException("userName can not be null", IHeader.SC_INVALID);
            }

            //初始化请求对象
            initRequest (request, session);

            //验证登录
            validateRepetaRequest (request, session);


            if (this.acceptor.getSessionListener() != null) {
                // 判断 是否为第一次连接
                if (!this.acceptor.isExist(request.getHeader().getAuthUser())) {
                    this.acceptor.getSessionListener()
                            .sessionCreate(request);
                    if (log.isInfoEnabled ()) {
                        log.info (request.getHeader ().getAuthUser () + " comming...");
                    }
                }
                this.acceptor.getSessionListener().messageReceived(request,response);
            }
            // 添加到映射
            this.acceptor.addMapping(request.getHeader().getAuthUser(), session
                    .getId());
            session.setAttribute(USER_KEY, request.getHeader().getAuthUser());

            if (request.getBody() != null && request.getBody().length > 0) {
                response.setStatus(IHeader.SC_OK);
                response.setBody(null);
                response.flush();
            }
        } catch (PushException e) {
            sendException(response,e);
            session.close(false);
        } catch (Exception e) {
            if (log.isErrorEnabled ()) {
                log.error("receive message error", e);
            }
            session.close(true);
        } finally {
            if (null != response) {
                response.destroy();
            }
            if(null!=request){
                request.destroy();
            }

        }
    }

    /**
     * 验证是否重复登录 假如 同一个用户在不同地方 登陆那么关闭之前的会话 保留新建会话
     */
    private void validateRepetaRequest(Request request, IoSession session) {
        IoSession oldSession = this.acceptor.getSession(request.getHeader()
                .getAuthUser());

        if (null != oldSession && oldSession.getId() != session.getId()) {
            //重复登录，向原有客户端发送错误信息
            try {
                Response response = new Response (oldSession);
                response.setStatus(IHeader.SC_REPEAT_REQUEST);
                response.flush();
                response.destroy();
                //移除映射
                this.acceptor.removeMapping(request.getHeader()
                        .getAuthUser());

                oldSession.close(true);

            } catch (Exception e) {
                if (log.isErrorEnabled ()) {
                    log.error (e);
                }
            }

        }
    }

    /**
     * 初始化请求
     * @param request
     * @param ioSession
     */
    private void initRequest(Request request, IoSession ioSession) {
        ISession session = null;

        if (ioSession != null) {
            request.setRemoteAddress (ioSession.getRemoteAddress ().toString ());
            session = (ISession) ioSession.getAttribute(SESSION_KEY);
        }

        if (null == session) {
            session = new Session();
        }

        request.setSession(session);
    }

    /**
     * 向客户端发送异常消息
     * @param response
     * @param e
     * @throws IOException
     */
    private void sendException(Response response,PushException e) throws IOException {
        response.destroy();
        response.setStatus(e.getStatus());
        if(null != e.getMessage()){
            response.setBody(e.getMessage().getBytes(IHeader.HEADER_ENCODING_CHARSET));
        }
        response.flush();


    }
}
